package com.xinongbest.mall.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.xinongbest.mall.service.TranslateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Service
public class TranslateServiceImpl implements TranslateService {
    private static final String NULL_ARR = "[]";

    private static final String NULL_OBJECT = "{}";

    @Value("${baidu.translate.api.appId}")
    private String APP_ID;

    @Value("${baidu.translate.api.appSecret}")
    private String APP_SECRET;

    private static final Pattern pattern = Pattern.compile("[\u4e00-\u9fa5]");

    private final Map<String, String> pathToTranslateMap = new HashMap<>();

    @Autowired
    private RestTemplate restTemplate;

    @Override
    public String translateApi(String json) {
        if (!StringUtils.isEmpty(json) && !NULL_ARR.equals(json) && !NULL_OBJECT.equals(json)) {
            Map<String, String> zhContent = parseZhContent(json);
            List<Map<String, String>> zhMapList = checkTranslateTextLength(zhContent);
            for (Map<String, String> stringMap : zhMapList) {
                String translateResult = requestTranslateApi(stringMap);
                handleTranslateMap(translateResult);
            }
            // 替换中文
            json = transferChToEn(json, zhContent);
        }
        return json;
    }

    private void handleTranslateMap(String apiResponse) {
        if (!StringUtils.isEmpty(apiResponse)) {
            JSONArray jsonArray = JSON.parseArray(apiResponse);
            JSONObject object;
            for (Object obj : jsonArray) {
                object = (JSONObject)obj;
                this.pathToTranslateMap.put(md5Hex(object.getString("src")), object.getString("dst"));
            }
        }
    }

    private MultiValueMap<String, String> handleRequestParam(Map<String, String> data) {
        LinkedMultiValueMap<String, String> result = new LinkedMultiValueMap<>();
        String q = handleQueryString(data);
        result.add("q", q);
        result.add("from", "zh");
        result.add("to", "en");
        result.add("appid", APP_ID);
        String salt = String.valueOf(System.currentTimeMillis());
        result.add("salt", salt);
        result.add("sign", handleSignature(q, salt));
        return result;
    }

    private String handleSignature(String data, String salt) {
        StringBuilder result = new StringBuilder();
        String signStr = result.append(APP_ID).append(data).append(salt).append(APP_SECRET).toString();
        return md5Hex(signStr);
    }

    private static String md5Hex(String data) {
        return DigestUtils.md5DigestAsHex(data.getBytes(StandardCharsets.UTF_8));
    }

    private String handleQueryString(Map<String, String> data) {
        StringBuilder builder = new StringBuilder();
        for (Map.Entry<String, String> entry : data.entrySet()) {
            builder.append(entry.getValue()).append("\n");
        }
        return builder.toString();
    }

    public String requestTranslateApi(Map<String, String> data) {
        String url = "https://fanyi-api.baidu.com/api/trans/vip/translate";
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/x-www-form-urlencoded");
        MultiValueMap<String, String> valueMap = handleRequestParam(data);
        HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(valueMap, headers);
        ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, httpEntity, String.class);
        int statusCode = response.getStatusCodeValue();
        if (statusCode == 200) {
            JSONObject jsonObject = JSON.parseObject(response.getBody());
            if (jsonObject.containsKey("error_code")) {
                return jsonObject.toJSONString();
            } else {
                return jsonObject.getString("trans_result");
            }
        } else {
            return "";
        }
    }

    /**
     * 翻译接口文本不能超过5000长度
     * @param stringMap 路径和中文内容映射
     * @return 拆分结果
     */
    private List<Map<String, String>> checkTranslateTextLength(Map<String, String> stringMap) {
        List<Map<String, String>> result = new ArrayList<>();
        Map<String, String> partMap = new HashMap<>();
        int length = 0;
        int currentLength;
        int maxLength = 2000;
        for (Map.Entry<String, String> entry : stringMap.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            currentLength = value.length();
            length += currentLength;
            if (currentLength >= maxLength) {
                continue;
            }
            if (length >= maxLength) {
                result.add(partMap);
                partMap = new HashMap<>();
                length = currentLength;
            }
            partMap.put(key, value);
        }
        if (!partMap.isEmpty()) {
            result.add(partMap);
        }
        return result;
    }

    private Map<String, String> parseZhContent(String json) {
        Map<String, String> result = new HashMap<>();
        Object object = JSON.parse(json);
        if (object instanceof JSONObject) {
            // 解析JSON结构
            for (Map.Entry<String, Object> entry : ((JSONObject)object).entrySet()) {
                String key = entry.getKey();
                Object value = entry.getValue();
                parseContents("", key, value, result);
            }
        }
        if (object instanceof JSONArray) {
            JSONArray obj = (JSONArray)object;
            for (int i = 0; i < obj.size(); i++) {
                parseContents("", String.valueOf(i), obj.get(i), result);
            }
        }
        return result;
    }

    /**
     * 递归解析出JSON结构中的中文字符
     * @param parentKey 父级key
     * @param key 当前key
     * @param object 当前值
     * @param map 路径和值映射
     */
    private void parseContents(String parentKey, String key, Object object, Map<String, String> map) {
        if (object instanceof String) {
            String value = (String) object;
            Matcher m = pattern.matcher(value);
            if (m.find()) {
                map.put(parentKey + "|" + key, value);
            }
        }
        if (object instanceof JSONObject) {
            for (Map.Entry<String, Object> entry : ((JSONObject) object).entrySet()) {
                parseContents(parentKey + "|obj:" + key, entry.getKey(), entry.getValue(), map);
            }
        }
        if (object instanceof JSONArray) {
            JSONArray obj = (JSONArray) object;
            for (int i = 0; i < obj.size(); i++) {
                parseContents(parentKey + "|arr:" + key + ":" + i, String.valueOf(i), obj.get(i), map);
            }
        }
    }

    private void handleObject(JSONObject object, String path, String value) {
        int index = path.indexOf("|", 1);
        if (index == -1) {
            String md5Key = md5Hex(value);
            if (this.pathToTranslateMap.containsKey(md5Key)) {
                object.put(path.replace("|", ""), this.pathToTranslateMap.get(md5Key));
            }
        } else {
            String current = path.substring(0, path.indexOf("|", 1));
            String nextPath = path.replace(current, "");
            String[] split = current.split(":");
            if ("|arr".equals(split[0])) {
                handleArray(object.getJSONArray(split[1]), nextPath, Integer.valueOf(split[2]),value);
            }
            if ("|obj".equals(split[0])) {
                handleObject(object.getJSONObject(split[1]), nextPath, value);
            }
        }
    }

    private void handleArray(JSONArray object, String path, Integer index, String value) {
        int ind = path.indexOf("|", 1);
        if (ind == -1) {
            String md5Key = md5Hex(value);
            if (this.pathToTranslateMap.containsKey(md5Key)) {
                object.set(index, this.pathToTranslateMap.get(md5Key));
            }
        } else {
            String current = path.substring(0, path.indexOf("|", 1));
            String nextPath = path.replace(current, "");
            String[] split = current.split(":");
            if ("|arr".equals(split[0])) {
                handleArray(object.getJSONArray(index), nextPath, Integer.valueOf(split[2]), value);
            }
            if ("|obj".equals(split[0])) {
                handleObject(object.getJSONObject(index), nextPath, value);
            }
        }
    }

    private String transferChToEn(String json, Map<String, String> zhContent) {
        JSONObject jsonObject = JSON.parseObject(json);
        for (Map.Entry<String, String> entry : zhContent.entrySet()) {
            handleObject(jsonObject, entry.getKey(), entry.getValue());
        }
        return jsonObject.toString(SerializerFeature.WriteMapNullValue);
    }
}
